import DataCenter from "../DataCenter";
import ThemeManager from "../ThemeManager";
import GameMinoFactory from "./GameMino";
import { Mino } from "./Mino";
import SevenBagSystem from "./SevenBagSystem";

const { ccclass, property } = cc._decorator;

@ccclass
export default class Board extends cc.Component {

    board: cc.Node[][] = [];
    mino: Mino = null;

    @property(cc.Node)
    MinoTemplate: cc.Node = null;

    @property(cc.Node)
    BoardRoot: cc.Node = null;

    protected onLoad(): void {
        this.initBoard();
    }

    initBoard() {
        const Col = DataCenter.Col;
        const Row = DataCenter.Row;

        for (let i = 0; i < Row + 5; i++) {
            this.board.push([]);
            for (let j = 0; j < Col; j++) {
                this.board[i].push(null);
            }
        }
    }

    clearAllMinos() {
        this.board.forEach(row => {
            row.forEach((item, idx, arr) => {
                arr[idx] = null;
                item && item.removeFromParent();
            });
        });

        if (this.mino) {
            this.mino.minoList.forEach(item => {
                item && item.removeFromParent();
            });
            this.mino.minoList.length = 0;
            this.mino = null;
        }
    }

    doCreateOneMino() {
        const Row = DataCenter.Row;
        const Col = DataCenter.Col;

        const type = SevenBagSystem.getMino();
        const mino = GameMinoFactory.createMino(type);

        mino.center.x = Col >> 1;
        mino.center.y = Row;

        const color = ThemeManager.getColor(type);
        mino.createMino(4, this.MinoTemplate, this.BoardRoot, color);

        mino.updatePos();

        this.mino = mino;
    }

    onMinoDie(mino: Mino) {
        const board = this.board;

        const posIndex = mino.getPosIndex(mino.angle, mino.center);
        const list = mino.minoList;

        for (let i = 0, len = posIndex.length; i < len; i++) {
            const pos = posIndex[i];
            board[pos.y][pos.x] = list[i];
        }
        list.length = 0;
    }

    clearLines(lineIdxs: number[]) {
        const Col = DataCenter.Col;

        const board = this.board;
        const items: cc.Node[][] = [];

        for (let i = 0; i < lineIdxs.length; i++) {
            const rowIdx = lineIdxs[i] - i;
            const row = board[rowIdx];

            row.forEach(item => {
                items[i] = items[i] || [];
                items[i].push(item);
            });

            board.splice(rowIdx, 1);
            
            var newRow = [];
            for(let j = 0; j < Col; j++) {
                newRow.push(null);
            }
            board.push(newRow);
        }

        // TODO 消一行，下降一行

        for (let i = 0; i < items.length; i++) {
            const row = items[i];
            const delay = i * 0.15 + 0.15;

            // console.log(`delay: ${delay}`);

            row.forEach(item => {
                item && item.removeFromParent();
            });

            // this.scheduleOnce(() => {
            //     row.forEach(item => {
            //         item && item.removeFromParent();
            //     });
            // }, delay);
        }

        const width = DataCenter.Width + DataCenter.SPACING;
        this.scheduleOnce(() => {
            board.forEach((row, rowIdx) => {
                row.forEach((item, colIdx) => {
                    if (item) {
                        item.setPosition(width * colIdx, width * rowIdx);
                    }
                });
            });
        }, .2);
    }

    toTurn() {
        const mino = this.mino;
        if (!mino) return;

        if (this.checkTurnIllegal(mino)) {
            return;
        }

        mino.setAngle(mino.getNextAngle());
        mino.updatePos();
    }

    toGoLeft() {
        const mino = this.mino;
        if (!mino) return;

        if (this.checkLeftIllegal(mino)) {
            return;
        }

        mino.left();
        mino.updatePos();
    }

    toGoRight() {
        const mino = this.mino;
        if (!mino) return;

        if (this.checkRightIllegal(mino)) {
            return;
        }

        mino.right();
        mino.updatePos();
    }

    toGoDown() {
        const mino = this.mino;
        if (!mino) return;

        let step = 2;

        if (this.checkDownIllegal(mino, step)) {
            step--;
            if (this.checkDownIllegal(mino, step)) {
                return;
            }
        }

        return mino;
    }

    checkGameOver(mino: Mino) {
        // const Col = DataCenter.Col;
        const Row = DataCenter.Row;

        const center = mino.center;
        const newCenter = cc.v2(center.x, center.y);

        const posIndex = mino.getPosIndex(mino.angle, newCenter);

        return posIndex.some(pos => {
            return pos.y >= Row;
        })
    }

    checkDownIllegal(mino: Mino, step: number = 1) {
        const center = mino.center;
        const newCenter = cc.v2(center.x, center.y - step);
        const posIndex = mino.getPosIndex(mino.angle, newCenter);

        return posIndex.some(pos => {
            return pos.y < 0 || this.hasMinoInBoard(pos);
        });
    }

    checkLeftIllegal(mino: Mino) {
        const center = mino.center;

        const nextCenter = cc.v2(center.x - 1, center.y);
        const posIndex = mino.getPosIndex(mino.angle, nextCenter);

        return posIndex.some(pos => {
            return pos.x < 0 || this.hasMinoInBoard(pos);
        });
    }

    checkRightIllegal(mino: Mino) {
        const Col = DataCenter.Col;
        // const Row = DataCenter.Row;

        const center = mino.center;

        const nextCenter = cc.v2(center.x + 1, center.y);
        const posIndex = mino.getPosIndex(mino.angle, nextCenter);

        return posIndex.some(pos => {
            return pos.x >= Col || this.hasMinoInBoard(pos);
        });
    }

    checkTurnIllegal(mino: Mino) {
        const Col = DataCenter.Col;
        // const Row = DataCenter.Row;

        const center = mino.center;
        const angle = mino.getNextAngle();

        const posIndex = mino.getPosIndex(angle, center);

        // console.log(posIndex);
        return posIndex.some(pos => {
            return pos.x < 0 || pos.x >= Col || pos.y < 0 || this.hasMinoInBoard(pos);
        })
    }

    hasMinoInBoard(pos: cc.Vec2) {
        const row = this.board[pos.y];
        return row && !!row[pos.x];
    }

    getFullLine() {
        const board = this.board;

        const row = 14;
        const fullRowIdx = [];
        for (let i = 0; i < row; i++) {
            const row = board[i];

            var isFull = row.every(item => !!item);
            if (isFull) {
                fullRowIdx.push(i);
            }
        }
        return fullRowIdx;
    }
}
